package com.example.tourism.utils;

import com.example.tourism.utils.API.drive_API;
import com.example.tourism.utils.API.ride_API;
import com.example.tourism.utils.API.walk_API;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class routing {
    private String origins;  //起点，格式为：纬度,经度|纬度,经度
    private String destinations;  //目标终点，格式为：纬度,经度|纬度,经度
    private String tactics;  //偏好设置
    private String riding_type;  //骑行设置   骑行类型选择，可选值如下：可选值：0 普通自行车 1 电动自行车
    private String AK; // ak密钥
    private int startingCity; //起始点，默认第一个经纬度参数是起始点
    private int cityNum;  //景点数量
    private int[][] driveDistance , driveTime;

    public int[][] getDriveDistance() {
        return driveDistance;
    }

    public int[][] getDriveTime() {
        return driveTime;
    }

    public int[][] getRideDistance() {
        return rideDistance;
    }

    public int[][] getRideTime() {
        return rideTime;
    }

    public int[][] getWalkDistance() {
        return walkDistance;
    }

    public int[][] getWalkTime() {
        return walkTime;
    }

    private int[][] rideDistance , rideTime;
    private int[][] walkDistance , walkTime;
    private int[][] bestDistance , bestTime;
    private List<Integer> tour_dis , tour_time;  //TSP算法后接收的返回值，为路径
    private int len_dis , len_time;  //TSP算法后接收的返回值，为路径长度或耗时
    private List<Integer> mode_dis , mode_time;  //根据TSP的返回值，计算出对应的行驶方式
    private int dis_time , time_dis;  //分别是距离最优下的时间耗时，时间最优下的距离长度

    public int getDis_time() {
        return dis_time;
    }
    public int getTime_dis() {
        return time_dis;
    }
    public List<Integer> getTour_dis() {
        return tour_dis;
    }
    public List<Integer> getTour_time() {
        return tour_time;
    }
    public int getLen_time() {
        return len_time;
    }
    public int getLen_dis() {
        return len_dis;
    }
    public List<Integer> getMode_dis() {
        return mode_dis;
    }
    public List<Integer> getMode_time() {
        return mode_time;
    }

    /**
     * @param locations 坐标字符串
     * @param tactics 驾车偏好，默认为13
     * @param riding_type 骑行类型，默认为0
     * @param AK AK密钥
     * @param cityNum 城市数量
     */
    public routing(String locations, String tactics, String riding_type, String AK, int cityNum){
        this.origins = locations;
        this.destinations = locations;
        this.tactics = tactics;
        this.riding_type =riding_type;
        this.AK = AK;
        this.cityNum = cityNum;
        this.startingCity = 0;
        this.tour_dis = new ArrayList<>();
        this.tour_time = new ArrayList<>();
        this.mode_dis = new ArrayList<>();
        this.mode_time = new ArrayList<>();
    }
    public void run() throws Exception {
        bestDistance = new int[cityNum][cityNum];
        bestTime = new int[cityNum][cityNum];
        for (int[] row : bestDistance) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
        for (int[] row : bestTime) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
        runDriveCost();
        runRideCost();
        runWalkCost();
        chooseBest();
        //记录出行方式
        TSP tsp_Dis = new TSP(bestDistance),
                tsp_Time = new TSP(bestTime);
        tsp_Dis.run(0);  //目前默认起始点为0号结点
        tsp_Time.run(0);
        tour_dis = tsp_Dis.getPath();  //路程，list，距离优先
        len_dis = tsp_Dis.getLength();  //距离优先  距离总长度
        tour_time = tsp_Time.getPath();  //路程，list，时间优先
        len_time = tsp_Time.getLength();  //时间优先  距离总长度
        mode_dis = getMode(tour_dis,rideDistance,walkDistance,bestDistance);  //距离优先下的行驶方式
        mode_time = getMode(tour_time,rideTime,walkTime,bestTime);
        dis_time = getOtherDetail(driveTime,rideTime,walkTime,mode_dis,tour_dis);
        time_dis = getOtherDetail(driveDistance,rideDistance,walkDistance,mode_time,tour_time);
//        print_show();
    }
    public void runAfterAPI(){
        //去除最后一行和最后一列
        rideDistance = removeLastRowAndColumn(rideDistance);
        rideTime = removeLastRowAndColumn(rideTime);
        walkDistance = removeLastRowAndColumn(walkDistance);
        walkTime = removeLastRowAndColumn(walkTime);
        driveDistance = removeLastRowAndColumn(driveDistance);
        driveTime = removeLastRowAndColumn(driveTime);
        bestDistance = removeLastRowAndColumn(bestDistance);
        bestTime = removeLastRowAndColumn(bestTime);
        cityNum--;
        //结束去除最后一行和最后一列

        TSP tsp_Dis = new TSP(bestDistance),
                tsp_Time = new TSP(bestTime);
        tsp_Dis.run(0);  //目前默认起始点为0号结点
        tsp_Time.run(0);
        tour_dis = tsp_Dis.getPath();  //路程，list，距离优先
        len_dis = tsp_Dis.getLength();  //距离优先  距离总长度
        tour_time = tsp_Time.getPath();  //路程，list，时间优先
        len_time = tsp_Time.getLength();  //时间优先  距离总长度
        mode_dis = getMode(tour_dis,rideDistance,walkDistance,bestDistance);  //距离优先下的行驶方式
        mode_time = getMode(tour_time,rideTime,walkTime,bestTime);
        dis_time = getOtherDetail(driveTime,rideTime,walkTime,mode_dis,tour_dis);
        time_dis = getOtherDetail(driveDistance,rideDistance,walkDistance,mode_time,tour_time);
//        print_show();
    }
    // 去除二维数组的最后一列和最后一行
    private static int[][] removeLastRowAndColumn(int[][] array) {
        int numRows = array.length - 1;
        int numCols = array[0].length - 1;

        int[][] result = new int[numRows][numCols];

        for (int i = 0; i < numRows; i++) {
            System.arraycopy(array[i], 0, result[i], 0, numCols);
        }

        return result;
    }
    private void print_show(){  //打印函数，看看参数情况
        System.out.println("最短行驶距离情况：");
        System.out.println("行驶路径为："+tour_dis);
        System.out.println("行驶方式为："+mode_dis);
        System.out.println("距离为："+len_dis);
        System.out.println("时长为："+dis_time);
        System.out.println("——————————————————————————————————————————————————————————————————————————————————————————————————————————————");
        System.out.println("最短行驶时间情况：");
        System.out.println("行驶路径为："+tour_time);
        System.out.println("行驶方式为："+mode_time);
        System.out.println("距离为："+time_dis);
        System.out.println("时长为："+len_time);
        System.out.println("——————————————————————————————————————————————————————————————————————————————————————————————————————————————");
        System.out.println(Arrays.deepToString(bestDistance));
        System.out.println(Arrays.deepToString(bestTime));
    }
    private int getOtherDetail(int[][] drive,int[][] ride , int[][] walk ,List<Integer> mode ,List<Integer> route){
        int cnt = 0;
        for(int i=0;i<mode.size();++i){
            switch (mode.get(i)){
                case 0 -> cnt +=drive[route.get(i)][route.get(i+1)];
                case 1 -> cnt +=ride[route.get(i)][route.get(i+1)];
                default -> cnt +=walk[route.get(i)][route.get(i+1)];
            }
        }
        return cnt;
    }
    private List<Integer> getMode(List<Integer> list , int[][] ride , int[][] walk ,int[][] best){
        List<Integer> mode = new ArrayList<>();
        for(int i=1;i<list.size();++i){
            int a = list.get(i-1) , b = list.get(i);
            int modeItem = 0;     //  0 为开车  ;   1 为骑车   ;   2为步行
            if(walk!=null&&walk[a][b]==best[a][b]){
                modeItem = 2;
            }
            else if(ride!=null&&ride[a][b]==best[a][b]){
                modeItem = 1;
            }
            mode.add(modeItem);
        }
        return mode;
    }
    private void chooseBest(){
        if(driveDistance!=null) {
            resoleBest(bestDistance, driveDistance);
        }
        if(rideDistance!=null) {
            resoleBest(bestDistance, rideDistance);
        }
        if(walkDistance!=null) {
            resoleBest(bestDistance, walkDistance);
        }
        if(driveTime!=null) {
            resoleBest(bestTime, driveTime);
        }
        if(rideTime!=null) {
            resoleBest(bestTime, rideTime);
        }
        if(walkTime!=null) {
            resoleBest(bestTime, walkTime);
        }
        for(int i=0;i<cityNum;++i){
            bestDistance[i][i]=0;
            bestTime[i][i]=0;
        }
    }
    private void resoleBest(int[][] best, int[][] temp){
        for(int i=0;i<cityNum;++i){
            for(int j=0;j<cityNum;++j){
                best[i][j]=Math.min(best[i][j],temp[i][j]);
            }
        }
    }
    private void runDriveCost() throws Exception {
        String driveStr = new drive_API(origins,destinations,tactics,AK).getResponse();
        JSONObject jsonObject = new JSONObject(driveStr);
        int status = jsonObject.getInt("status");
        if(status!=0){
            return ;
        }
        JSONArray resultArray = jsonObject.getJSONArray("result");
        this.driveDistance = new int[cityNum][cityNum];
        this.driveTime = new int[cityNum][cityNum];
        getValueByJson(driveDistance,driveTime,resultArray);
    }
    private void runRideCost() throws Exception {
        String rideStr = new ride_API(origins,destinations,riding_type,AK).getResponse();
        JSONObject jsonObject = new JSONObject(rideStr);
        int status = jsonObject.getInt("status");
        if(status!=0){
            return ;
        }
        JSONArray resultArray = jsonObject.getJSONArray("result");
        this.rideDistance = new int[cityNum][cityNum];
        this.rideTime = new int[cityNum][cityNum];
        getValueByJson(rideDistance,rideTime,resultArray);
    }
    private void runWalkCost() throws Exception {
        String walkStr = new walk_API(origins,destinations,AK).getResponse();
        JSONObject jsonObject = new JSONObject(walkStr);
        int status = jsonObject.getInt("status");
        if(status!=0){
            return ;
        }
        JSONArray resultArray = jsonObject.getJSONArray("result");
        this.walkDistance = new int[cityNum][cityNum];
        this.walkTime = new int[cityNum][cityNum];
        getValueByJson(walkDistance,walkTime,resultArray);
    }
    private void getValueByJson(int[][] dis , int[][] time , JSONArray resultArray){
        int row = 0 , col = 0;
        // 遍历 resultArray 中的元素
        for (int i = 0; i < resultArray.length(); i++) {
            JSONObject resultObject = resultArray.getJSONObject(i);
            JSONObject distanceObject = resultObject.getJSONObject("distance");
            JSONObject durationObject = resultObject.getJSONObject("duration");
            int distanceValue = (int) distanceObject.getDouble("value");
            int durationValue = (int) durationObject.getDouble("value");
            dis[row][col] = distanceValue;
            time[row][col] = durationValue;
            col++;
            if(col==cityNum){
                row++;
                col = 0;
            }
        }
    }
}
